/*---------------------------------------------------------------------
 * File name: hal_gpio.cpp
 *
 * Copyright (c) <2020-2022>, <ChenLong>
 *
 * All rights reserved.
 *
 * Author: ChenLong
 * Email: worldlong@foxmail.com
 *--------------------------------------------------------------------*/
#include "gd32f10x.h"
#include "hal_gpio.h"
#include "hal_config.h"
#if defined(USING_RT_THREAD)
#include "rtthread.h" 
#endif
/*
*/
static void (*irq_hook[16])(void *param) = {nullptr};
static void *irq_hook_param[16]={nullptr};
const static struct {
  uint8_t irqn;
  uint8_t priority;
}nvic_table[]={
  {EXTI0_IRQn, //EXTI0
   NVIC_IRQ_EXTI0_PRIORITY},
  {EXTI1_IRQn, //EXTI1
   NVIC_IRQ_EXTI1_PRIORITY},
  {EXTI2_IRQn, //EXTI2
   NVIC_IRQ_EXTI2_PRIORITY},
  {EXTI3_IRQn, //EXTI3
   NVIC_IRQ_EXTI3_PRIORITY},
  {EXTI4_IRQn, //EXTI4
   NVIC_IRQ_EXTI4_PRIORITY},
  {EXTI5_9_IRQn, //EXTI5~//EXTI9
   NVIC_IRQ_EXTI9_5_PRIORITY},
  {EXTI10_15_IRQn, //EXTI10 ~ EXTI15
   NVIC_IRQ_EXTI15_10_PRIORITY}
};
/*
*/
bool HAL_GPIO::init(uint64_t cfg0)
{ 
  cfg = cfg0;
  uint32_t tmp = HGPIO_CFG_EXTRACT(cfg, PORT);
  uint8_t exti_port_source = 0;
  
  if(tmp == 0) { //No port
    return false;
  }
#if GPIOA_ENABLE
  else if(tmp == HGPIO_PORT(A)) {
    port = (uint32_t)GPIOA;
    exti_port_source = GPIO_PORT_SOURCE_GPIOA;
  }
#endif
#if GPIOB_ENABLE
  else if(tmp == HGPIO_PORT(B)) {
    port = (uint32_t)GPIOB;
    exti_port_source = GPIO_PORT_SOURCE_GPIOB;
  } 
#endif
#if GPIOC_ENABLE
  else if(tmp == HGPIO_PORT(C)) {
    port = (uint32_t)GPIOC;
    exti_port_source = GPIO_PORT_SOURCE_GPIOC;
  }
#endif
#if GPIOD_ENABLE
  else if(tmp == HGPIO_PORT(D)) {
    port = (uint32_t)GPIOD;
    exti_port_source = GPIO_PORT_SOURCE_GPIOD;
  }
#endif
#if GPIOE_ENABLE
  else if(tmp == HGPIO_PORT(E)) {
    port = (uint32_t)GPIOE;
    exti_port_source = GPIO_PORT_SOURCE_GPIOE;
  }
#endif
#if GPIOF_ENABLE
  else if(tmp == HGPIO_PORT(F)) {
    port = (uint32_t)GPIOF;
    exti_port_source = GPIO_PORT_SOURCE_GPIOF;
  } 
#endif
#if GPIOG_ENABLE
  else if(tmp == HGPIO_PORT(G)) {
    port = (uint32_t)GPIOG;
    exti_port_source = GPIO_PORT_SOURCE_GPIOG;
  } 
#endif
#if GPIOH_ENABLE
  else if(tmp == HGPIO_PORT(H)) {
    port = (uint32_t)GPIOH;
    exti_port_source = GPIO_PORT_SOURCE_GPIOH;
  } 
#endif
  else if(port == 0){
    return false;
  }
  
  if(!config(cfg)) {
    return false;
  }
  
  //EXTI NVIC
  tmp = HGPIO_CFG_EXTRACT(cfg, EXTI);
  if(tmp != HGPIO_EXTI(NONE))
  {
    exti_line_enum EXTI_LINE = (exti_line_enum)BIT(_index);
    /* Connect EXTI line to GPIO pin */
    gpio_exti_source_select(exti_port_source, _index);
    /* Configure EXTI line */
    switch(tmp)
    {
    case HGPIO_EXTI(INTRT):
      exti_init(EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_RISING);
      break;
    case HGPIO_EXTI(INTFT):
      exti_init(EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
      break;
    case HGPIO_EXTI(INTRFT):
      exti_init(EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_BOTH);
      break;
    case HGPIO_EXTI(EVTRT):
      exti_init(EXTI_LINE, EXTI_EVENT, EXTI_TRIG_RISING);
      break;
    case HGPIO_EXTI(EVTFT):
      exti_init(EXTI_LINE, EXTI_EVENT, EXTI_TRIG_FALLING);
      break;
    case HGPIO_EXTI(EVTRFT):
      exti_init(EXTI_LINE, EXTI_EVENT, EXTI_TRIG_BOTH);
      break;
    default:
      return false;
    }
    exti_interrupt_flag_clear(EXTI_LINE);
    //NVIC config
    int nvic_index = _index;
    if(nvic_index >= 5 && nvic_index <= 9) {
      nvic_index = 5;
    } else if(nvic_index >= 10 && nvic_index <= 15) {
      nvic_index = 6;
    } 
    nvic_irq_enable(nvic_table[nvic_index].irqn, nvic_table[nvic_index].priority, 0U);
  }
  return true;
}
/*
*/
bool HAL_GPIO::config(uint64_t cfg0)
{
  cfg &= ~HGPIO_CFG_MASK;
  cfg |= cfg0 & HGPIO_CFG_MASK;
 
  //pin
  pin = HGPIO_CFG_EXTRACT(cfg, PIN);
  if(pin == 0) {
    return false;
  }
  
  //pin number, just used for only one pin
  for(int i=0; i<16; i++) {
    if((pin >> i) & 0x01) {
      _index = i;
      break;
    }
  }
   
  //type
  uint32_t type = HGPIO_CFG_EXTRACT(cfg, TYPE);
  //speed
  uint8_t speed = 0;
  uint32_t tmp = HGPIO_CFG_EXTRACT(cfg, SPEED);
  if(tmp == HGPIO_SPEED(SLOW)) {
    speed = GPIO_OSPEED_2MHZ;
  } else if(tmp == HGPIO_SPEED(MEDIUM)) {
    speed = GPIO_OSPEED_10MHZ;
  } else { //FAST
    speed = GPIO_OSPEED_50MHZ;
  }
  
  //mode
  uint8_t mode = 0;
  tmp = HGPIO_CFG_EXTRACT(cfg, MODE);
  if(tmp == HGPIO_MODE(OUT)) {
    if(type == HGPIO_TYPE(PP)) {
      mode = GPIO_MODE_OUT_PP;
    } else{
      mode = GPIO_MODE_OUT_OD;
    }
  } else if(tmp == HGPIO_MODE(IN)) {
    if(type == HGPIO_TYPE(PU)) {
      mode = GPIO_MODE_IPU;
    } else if(type == HGPIO_TYPE(PD)) {
      mode = GPIO_MODE_IPD;
    } else {
      mode = GPIO_MODE_IN_FLOATING;
    }
  } else if(tmp == HGPIO_MODE(AN)) {
    mode = GPIO_MODE_AIN;
  } else { //AF
    if(type == HGPIO_TYPE(PP)) {
      mode = GPIO_MODE_AF_PP;
    } else {
      mode = GPIO_MODE_AF_OD;
    } 
  }
  gpio_init(port, mode, speed, pin);
 
  //status
  if(tmp == HGPIO_MODE(OUT)) {
    tmp = HGPIO_CFG_EXTRACT(cfg, STATUS);
    if(tmp == HGPIO_STATUS(SET)) {
      GPIO_BOP(port) = (uint32_t)pin;
    } else {
      GPIO_BC(port) = (uint32_t)pin;
    }
  }
  
  return true;
}
/*
*/
void HAL_GPIO::remap(uint32_t remap)
{
  gpio_pin_remap_config(remap, ENABLE);
}
/*
*/
uint16_t HAL_GPIO::read()
{
  if(port) {
    return gpio_input_port_get(port) & pin;
  }
  return 0;
}
/*
*/
void HAL_GPIO::write(uint16_t val)
{
  if(port) {
    GPIO_OCTL(port) = (GPIO_OCTL(port) & ~pin) | (val & pin);
  }
}
/*
*/
void HAL_GPIO::toggle()
{
  if(port) {
    GPIO_OCTL(port) = GPIO_OCTL(port) ^ pin;
  }
}
/*
*/
uint8_t HAL_GPIO::output_state()
{
  if(port) {
    return ((GPIO_OCTL(port))&pin) != 0;
  }
  return 0;
}
/*
*/
uint8_t HAL_GPIO::input_state()
{
  return read() != 0;
}
/*
*/
void HAL_GPIO::attach_irq(void(*fun)(void *param), void *param)
{
  irq_hook[_index] = fun;
  irq_hook_param[_index] = param;
}

/*************************************************
              IRQHandler
**************************************************/
#ifdef __cplusplus
 extern "C" {
#endif
/*
*/
void EXTI0_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(exti_interrupt_flag_get(EXTI_0) !=  RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_0); 
    if(irq_hook[0] != nullptr)
    {
      irq_hook[0](irq_hook_param[0]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI1_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif  
  if(exti_interrupt_flag_get(EXTI_1) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_1); 
    if(irq_hook[1] != nullptr)
    {
      irq_hook[1](irq_hook_param[1]);
    }
  }   
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI2_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(exti_interrupt_flag_get(EXTI_2) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_2); 
    if(irq_hook[2] != nullptr)
    {
      irq_hook[2](irq_hook_param[2]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI3_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(exti_interrupt_flag_get(EXTI_3) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_3); 
    if(irq_hook[3] != nullptr)
    {
      irq_hook[3](irq_hook_param[3]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI4_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(exti_interrupt_flag_get(EXTI_4) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_4); 
    if(irq_hook[4] != nullptr)
    {
      irq_hook[4](irq_hook_param[4]);
    }
  } 
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI5_9_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(exti_interrupt_flag_get(EXTI_5) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_5); 
    if(irq_hook[5] != nullptr)
    {
      irq_hook[5](irq_hook_param[5]);
    }
  }
  
  if(exti_interrupt_flag_get(EXTI_6) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_6); 
    if(irq_hook[6] != nullptr)
    {
      irq_hook[6](irq_hook_param[6]);
    }
  }  
  
  if(exti_interrupt_flag_get(EXTI_7) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_7); 
    if(irq_hook[7] != nullptr)
    {
      irq_hook[7](irq_hook_param[7]);
    }
  }  
  
  if(exti_interrupt_flag_get(EXTI_8) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_8); 
    if(irq_hook[8] != nullptr)
    {
      irq_hook[8](irq_hook_param[8]);
    }
  }  
  
  if(exti_interrupt_flag_get(EXTI_9) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_9); 
    if(irq_hook[9] != nullptr)
    {
      irq_hook[9](irq_hook_param[9]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI10_15_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(exti_interrupt_flag_get(EXTI_10) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_10); 
    if(irq_hook[10] != nullptr)
    {
      irq_hook[10](irq_hook_param[10]);
    }
  }
  
  if(exti_interrupt_flag_get(EXTI_11) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_11); 
    if(irq_hook[11] != nullptr)
    {
      irq_hook[11](irq_hook_param[11]);
    }
  }  

  if(exti_interrupt_flag_get(EXTI_12) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_12); 
    if(irq_hook[12] != nullptr)
    {
      irq_hook[12](irq_hook_param[12]);
    }
  }  
  
  if(exti_interrupt_flag_get(EXTI_13) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_13); 
    if(irq_hook[13] != nullptr)
    {
      irq_hook[13](irq_hook_param[13]);
    }
  }  

  if(exti_interrupt_flag_get(EXTI_14) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_14); 
    if(irq_hook[14] != nullptr)
    {
      irq_hook[14](irq_hook_param[14]);
    }
  }  
  
  if(exti_interrupt_flag_get(EXTI_15) != RESET)   
  { 
    exti_interrupt_flag_clear(EXTI_15); 
    if(irq_hook[15] != nullptr)
    {
      irq_hook[15](irq_hook_param[15]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
#ifdef __cplusplus
}
#endif




